#!/usr/bin/python2.7
#
# CDDL HEADER START
#
# The contents of this file are subject to the terms of the
# Common Development and Distribution License (the "License").
# You may not use this file except in compliance with the License.
#
# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
# or http://www.opensolaris.org/os/licensing.
# See the License for the specific language governing permissions
# and limitations under the License.
#
# When distributing Covered Code, include this CDDL HEADER in each
# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
# If applicable, add the following below this CDDL HEADER, with the
# fields enclosed by brackets "[]" replaced with your own identifying
# information: Portions Copyright [yyyy] [name of copyright owner]
#
# CDDL HEADER END
#
# Copyright (c) 2010, Oracle and/or it's affiliates.  All rights reserved.
#
#
# bass-o-matic.py
#  A simple program to enumerate components in the userland gate and report
#  on dependency related information.
#

import os
import sys
import re
import subprocess

# Locate SCM directories containing Userland components by searching from
# from a supplied top of tree for .p5m files.  Once a .p5m file is located,
# that directory is added to the list and no children are searched.
def FindComponentPaths(path, debug=None, subdir='/components'):
    expression = re.compile(".+\.p5m$", re.IGNORECASE)

    paths = []

    if debug:
        print >>debug, "searching %s for component directories" % path

    for dirpath, dirnames, filenames in os.walk(path + subdir):
        found = 0

        for name in filenames:
            if expression.match(name):
                if debug:
                    print >>debug, "found %s" % dirpath
                paths.append(dirpath)
                del dirnames[:]
                break

    return sorted(paths)

class BassComponent:
    def __init__(self, path=None, debug=None):
        self.debug = debug
        self.path = path
        if path:
            # get supplied packages    (cd path ; gmake print-package-names)
            self.supplied_packages = self.run_make(path, 'print-package-names')

            # get supplied paths    (cd path ; gmake print-package-paths)
            self.supplied_paths = self.run_make(path, 'print-package-paths')

            # get required paths    (cd path ; gmake print-required-paths)
            self.required_paths = self.run_make(path, 'print-required-paths')

    def required(self, component):
        result = False

        s1 = set(self.required_paths)
        s2 = set(component.supplied_paths)
        if s1.intersection(s2):
            result = True

        return result

    def run_make(self, path, targets):

        result = list()

        if self.debug:
            print >>self.debug, "Executing 'gmake %s' in %s" % (targets, path)

        proc = subprocess.Popen(['gmake', targets], cwd=path,
                                stdout=subprocess.PIPE, stderr=subprocess.PIPE)
        p = proc.stdout

        for out in p:
            result.append(out)

        if self.debug:
            proc.wait()
            if proc.returncode != 0:
                print >>self.debug, "exit: %d, %s" % (proc.returncode, proc.stderr.read())
    
        return result

    def __str__(self):
        result = "Component:\n\tPath: %s\n" % self.path
        result = result + "\tProvides Package(s):\n\t\t%s\n" % '\t\t'.join(self.supplied_packages)
        result = result + "\tProvides Path(s):\n\t\t%s\n" % '\t\t'.join(self.supplied_paths)
        result = result + "\tRequired Path(s):\n\t\t%s\n" % '\t\t'.join(self.required_paths)

        return result

def usage():
    print "Usage: %s [-c|--components=(path|depend)] [-z|--zone (zone)]" % (sys.argv[0].split('/')[-1])
    sys.exit(1)

def main():
    import getopt
    import sys

    # FLUSH STDOUT 
    sys.stdout = os.fdopen(sys.stdout.fileno(), 'w', 0)

    components = {}
    debug=None
    components_arg=None
    make_arg=None
    component_arg=None
    template_zone=None
    subdir="/components"
    workspace = os.getenv('WS_TOP')

    try:
        opts, args = getopt.getopt(sys.argv[1:], "w:c:d",
            [ "debug", "workspace=", "components=",
              "make", "component=", "template-zone=", "subdir=" ])
    except getopt.GetoptError, err:
        print str(err)
        usage()

    for opt, arg in opts:
        if opt in [ "-w", "--workspace" ]:
            workspace = arg
        elif opt in [ "-l", "--components" ]:
            components_arg = arg
        elif opt in [ "--make" ]:
            make_arg = True
        elif opt in [ "--component" ]:
            component_arg = arg
        elif opt in [ "--template-zone" ]:
            template_zone = arg
        elif opt in [ "--subdir" ]:
            subdir = arg
        elif opt in [ "-d", "--debug" ]:
            debug = sys.stdout
        else:
            assert False, "unknown option"

    component_paths = FindComponentPaths(workspace, debug, subdir)

    if make_arg:
        if template_zone:
            print "using template zone %s to create a build environment for %s to run '%s'" % (template_zone, component_arg, ['gmake'] + args)
        proc = subprocess.Popen(['gmake'] + args)
	rc = proc.wait()
        sys.exit(rc)

    if components_arg:
        if components_arg in [ 'path', 'paths', 'dir', 'dirs', 'directories' ]:
            for path in component_paths:
                print "%s" % path
            
        elif components_arg in [ 'depend', 'dependencies' ]:
            for path in component_paths:
                components[path] = BassComponent(path, debug)

            for c_path in components.keys():
                component = components[c_path]

                for d_path in components.keys():
                    if (c_path != d_path and
                        component.required(components[d_path])):
                          print "%s: %s" % (c_path, d_path)

        sys.exit(0)

    sys.exit(1)

if __name__ == "__main__":
    main()
